#pragma once
#include "SquareNode.h"
#include "Observer.h"
#include "SpaceAgent.h"
#include "Vector.h"
#include "Grid.h"
#include <string>
#include <map>
#include <vector>

/*
  Copyright 2008 by Alexey Solovyev and University of Pittsburgh
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

namespace org
{
	namespace spark
	{
		namespace space
		{

			class DataLayer;
			class SpaceAgent;
			class Grid;
			class Vector;

			//using org::spark::core::ExecutionMode;
			//using org::spark::core::Observer;
			//using org::spark::data::DataLayer;
			//using org::spark::data::Grid;
			//using org::spark::math::Vector;
			//using org::spark::space::SpaceAgent;


			/// <summary>
			/// The basic class for space.
			/// It is an abstract class which defines all generic functionality
			/// of arbitrary SPARK space 
			/// </summary>
			class Space 
			{
				/* Serial version UID */
			private:
				//static const long long serialVersionUID = 7300324697412064632LL;

				/* Reference to the observer */
				Observer *observer;

				/* Space index is a substitution for space's name for fast operations */
				int spaceIndex;

				std::vector<SpaceNode*> nodeQueue;
				const int executionMode;


				/* Determines the maximum size of all nodes in the space */
			protected:
				double maximumNodeRadius;


				/* Collection of all data layers in this space */ 
			private:
				std::map<std::string, DataLayer*> dataLayers;
				/* List of all data layers in this space */
				std::vector<DataLayer*> dataLayersList;



				/// <summary>
				/// Default constructor
				/// </summary>
			protected:
				Space();


				/// <summary>
				/// Sets space's index </summary>
				/// <param name="index"> </param>
				// TODO: should not be public
			public:
				virtual void setIndex(int index);


				/// <summary>
				/// Returns space's index
				/// @return
				/// </summary>
				virtual int getIndex();


				/// <summary>
				/// Adds a new data layer into the space </summary>
				/// <param name="name"> a name of a new data layer </param>
				/// <param name="data"> a data layer itself </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual T addDataLayer(const std::string &name, T data);


				/// <summary>
				/// Gets a specific data layer </summary>
				/// <param name="name"> a name of a data layer </param>
				/// <returns> null if no such a data layer </returns>
				virtual DataLayer *getDataLayer(const std::string &name);


				/// <summary>
				/// Returns names of all data layers in the space </summary>
				/// <returns> names of all data layers </returns>
				virtual std::string *getDataLayerNames();


				/// <summary>
				/// Processes all data layers </summary>
				/// <param name="tick"> </param>
				void processAllDataLayers(long long tick);



				/// <summary>
				/// Processes all nodes (in concurrent execution mode)
				/// </summary>
				void processNodes();


				/// <summary>
				/// Implement in a subclass for additional manual node processing
				/// </summary>
			protected:
				virtual void postProcess();


				
				/// <summary>
				/// Creates a circle node with a specific radius and for a given agent </summary>
				/// <param name="r"> </param>
				/// <param name="agent"> </param>
				virtual CircleNode *createCircleNode0(double r, int type);

				virtual void addNode0(SpaceNode *node) = 0;

				virtual void addNodeAndMove0(SpaceNode *node) = 0;

				/// <summary>
				/// Creates a square node of the given size r </summary>
				/// <param name="r">
				/// @return </param>
				SquareNode *createSquareNode(double r, SpaceAgent *agent);

				/// <summary>
				/// Creates a square node </summary>
				/// <param name="r"> </param>
				/// <param name="agent"> </param>
				virtual SquareNode *createSquareNode0(double r);


				/// <summary>
				/// Creates a square2 node of the given size r </summary>
				/// <param name="r">
				/// @return </param>
				//Square2Node *createSquare2Node(double r, SpaceAgent *agent);

				/// <summary>
				/// Creates a square2 node </summary>
				/// <param name="r"> </param>
				/// <param name="agent"> </param>
				//virtual Square2Node *createSquare2Node0(double r);

				/// <summary>
				/// Removes the node from the space </summary>
				/// <param name="node"> </param>
				void removeNode(SpaceNode *node);

				virtual void removeNode0(SpaceNode *node) = 0;

				/// <summary>
				/// Changes the position of the node in the space </summary>
				/// <param name="node"> </param>
				

				virtual void changeNodePosition0(SpaceNode *node) = 0;



			private:
				CircleNode *temp;

				/// <summary>
				/// Gets all agents in the specific circle neighborhood </summary>
				/// <param name="v"> - the center of the circle </param>
				/// <param name="radius"> - the radius of the circle
				/// @return </param>
			public:
				virtual std::vector<SpaceAgent*> getAgents(Vector *v, double radius);

				/// <summary>
				/// Gets all agents in the place occupied by the agent </summary>
				/// <param name="agent">
				/// @return </param>
				virtual std::vector<SpaceAgent*> getAgents(SpaceAgent *agent);

				/// <summary>
				/// Gets all agents intersecting with the given space node </summary>
				/// <param name="node">
				/// @return </param>
			protected:
				virtual std::vector<SpaceAgent*> getAgents(SpaceNode *node) = 0;

				/// <summary>
				/// Gets all agents in the specific circle neighborhood and of the given type </summary>
				/// <param name="v"> - the center of the circle </param>
				/// <param name="radius"> - the radius of the circle </param>
				/// <param name="type">
				/// @return </param>
			//	public ArrayList<SpaceAgent> getAgents(Vector v, double radius, 
			//			Class<? extends SpaceAgent> type) {
            public:
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual std::vector<T> getAgents(Vector *v, double radius, Class *type);


				/// <summary>
				/// Gets all agents in the specific circle neighborhood 
				/// which are derived from the given type </summary>
				/// <param name="v"> - the center of the circle </param>
				/// <param name="radius"> - the radius of the circle </param>
				/// <param name="type">
				/// @return </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T>
				virtual std::vector<T> getAgentsOfKind(Vector *v, double radius, Class *type);

				/// <summary>
				/// Gets all agents of the given type at the place occupied by the agent </summary>
				/// <param name="agent"> </param>
				/// <param name="type">
				/// @return </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual std::vector<T> getAgents(SpaceAgent *agent, Class *type);

				/// <summary>
				/// Gets all agents derived from the given type at the place occupied by the agent </summary>
				/// <param name="agent"> </param>
				/// <param name="type">
				/// @return </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual std::vector<T> getAgentsOfKind(SpaceAgent *agent, Class *type);


				/// <summary>
				/// Gets all agents of the given type intersecting with the space node </summary>
				/// <param name="node"> </param>
				/// <param name="type">
				/// @return </param>

				/// <summary>
				/// Creates a circle node of the given radius r </summary>
				/// <param name="r">
				/// @return </param>
				CircleNode *createCircleNode(double r, SpaceAgent *agent, int type);

            protected:
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual std::vector<T*> getAgents(SpaceNode *node, Class *type);

				/// <summary>
				/// Gets all agents derived from the given type intersecting with the space node </summary>
				/// <param name="node"> </param>
				/// <param name="type">
				/// @return </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
				virtual std::vector<T*> getAgentsOfKind(SpaceNode *node, Class *type) ;


				/// <summary>
				/// Returns the vector representing the distance between v1 and v2 </summary>
				/// <param name="v1"> </param>
				/// <param name="v2">
				/// @return </param>
			public:
				virtual Vector *getVector(Vector *v1, Vector *v2) = 0;

				/// <summary>
				/// Returns the vector representing the distance between agent1 and agent2 </summary>
				/// <param name="agent1"> </param>
				/// <param name="agent2">
				/// @return </param>
				virtual Vector *getVector(SpaceAgent *agent1, SpaceAgent *agent2);

				/// <summary>
				/// Returns the random point in the space
				/// @return
				/// </summary>
				virtual Vector *getRandomPosition() = 0;

				void changeNodePosition(SpaceNode *node);

				/// <summary>
				/// Calls the beginStep() method for all data layers
				/// </summary>
				virtual void dataLayersBeginStep();


				/// <summary>
				/// Calls the endStep() method for all data layers
				/// </summary>
				virtual void dataLayersEndStep();


				/// <summary>
				/// Serializes the space with data layers </summary>
				/// <param name="oos"> </param>
				//virtual void serializeSpace(ObjectOutputStream *oos); //throw(Exception);


				/// <summary>
				/// Loads the space with data layers </summary>
				/// <param name="ois"> </param>
				//static Space *loadSpace(ObjectInputStream *ois) //throw(Exception);


				virtual std::string ToString();


				/// <summary>
				/// Custom deserialization is needed.
				/// </summary>
			private:
				//void readObject(ObjectInputStream *ois) //throw(IOException, ClassNotFoundException);

				/// <summary>
				/// Custom serialization is needed.
				/// </summary>
				//void writeObject(ObjectOutputStream *oos) //throw(IOException);

			private:
				void InitializeInstanceFields();
			};

		}
	}
}
